package com.digiwin.dap.middleware;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.digiwin.dap.middleware.aspect.AutoSetEntityManagerFieldAspect;
import com.digiwin.dap.middleware.aspect.CascadeDeleteEntityRepositoryAspect;
import com.digiwin.dap.middleware.auth.JwtAppTokenService;
import com.digiwin.dap.middleware.auth.impl.JwtAppTokenServiceImpl;
import com.digiwin.dap.middleware.exception.GlobalExceptionHandler;
import com.digiwin.dap.middleware.service.AssociationEntityService;
import com.digiwin.dap.middleware.service.CascadeDeleteEntityService;
import com.digiwin.dap.middleware.service.LocaleMessageSourceService;
import com.digiwin.dap.middleware.service.RelationAssociationEntityService;
import com.digiwin.dap.middleware.service.impl.AssociationEntityServiceImpl;
import com.digiwin.dap.middleware.service.impl.CascadeDeleteEntityServiceImpl;
import com.digiwin.dap.middleware.service.impl.LocaleMessageSourceServiceImpl;
import com.digiwin.dap.middleware.service.impl.RelationAssociationEntityServiceImpl;
import com.digiwin.dap.middleware.trace.AutoLogFileWebEndpoint;
import com.digiwin.dap.middleware.util.JsonUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.logging.LogFileWebEndpoint;
import org.springframework.boot.actuate.trace.http.HttpExchangeTracer;
import org.springframework.boot.actuate.trace.http.HttpTraceRepository;
import org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author chenxsa
 */
@ConditionalOnWebApplication
@Configuration
public class DataCoreAutoConfiguration {
    @Bean
    public LogFileWebEndpoint logFileWebEndpoint(Environment environment) {
        return new AutoLogFileWebEndpoint(environment);
    }

    @Bean
    public HttpTraceFilter httpTraceFilter(HttpTraceRepository repository, HttpExchangeTracer tracer) {
        return new HttpTraceFilter(repository, tracer);
    }

    @Bean
    @ConditionalOnMissingBean(AssociationEntityService.class)
    public AssociationEntityService associationEntityService() {
        AssociationEntityServiceImpl associationEntityService = new AssociationEntityServiceImpl();
        return associationEntityService;
    }
    @Bean
    @ConditionalOnMissingBean(GlobalExceptionHandler.class)
    GlobalExceptionHandler globalExceptionHandler(){
        return  new GlobalExceptionHandler();
    }

    @Bean
    @ConditionalOnMissingBean(JwtAppTokenService.class)
    JwtAppTokenService jwtAppTokenService(){
        return  new JwtAppTokenServiceImpl();
    }

    @Bean
    AutoSetEntityManagerFieldAspect autoSetEntityManagerFieldAspect(){
        return  new AutoSetEntityManagerFieldAspect();
    }

    @Bean
    CascadeDeleteEntityRepositoryAspect cascadeDeleteEntityRepositoryAspect(){
        return  new CascadeDeleteEntityRepositoryAspect();
    }

    @Value("${dap.middleware.languageCategory:zh_CN}")
    private  String localLang;

    @Bean
    @ConditionalOnMissingBean(LocaleMessageSourceService.class)
    public LocaleMessageSourceService localeMessageSourceService() {

        LocaleMessageSourceServiceImpl localeMessageSourceService = new LocaleMessageSourceServiceImpl();
        localeMessageSourceService.setLocalLang(localLang);
        return localeMessageSourceService;
    }

    @Bean
    @ConditionalOnMissingBean(RelationAssociationEntityService.class)
    public RelationAssociationEntityService relationAssociationEntityService() {
        RelationAssociationEntityServiceImpl relationAssociationEntityService = new RelationAssociationEntityServiceImpl();
        return relationAssociationEntityService;
    }

    @Bean
    @ConditionalOnMissingBean(CascadeDeleteEntityService.class)
    public CascadeDeleteEntityService cascadeDeleteEntityService() {
        CascadeDeleteEntityServiceImpl cascadeDeleteEntityService = new CascadeDeleteEntityServiceImpl();
        return cascadeDeleteEntityService;
    }

    @Bean
    @Primary
    public ObjectMapper jacksonObjectMapper() {
        return JsonUtils.createObjectMapper();
    }

    @Bean
    @Primary
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        // 设置日期格式
        mappingJackson2HttpMessageConverter.setObjectMapper(JsonUtils.createObjectMapper());
        return mappingJackson2HttpMessageConverter;
    }

    /**
     * 返回自定义的RestTemplate
     *
     * @return
     */
    @Bean
    @Primary
    public RestTemplate restTemplate(ObjectMapper objectMapper) {
        RestTemplate restTemplate = new RestTemplate();
        for (HttpMessageConverter<?> httpMessageConverter : restTemplate.getMessageConverters()) {
            if (MappingJackson2HttpMessageConverter.class.isInstance(httpMessageConverter)) {
                ((MappingJackson2HttpMessageConverter) httpMessageConverter).setObjectMapper(objectMapper);
            }
        }
        return restTemplate;
    }

    @Value("${async.executor.thread.core_pool_size:400}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size:1000}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity:99999}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix:async-service-}")
    private String namePrefix;

    @Bean
    @Primary
    public Executor asyncServiceExecutor() {
        /*
        每个任务需要tasktime秒处理，则每个线程每钞可处理1/tasktime个任务。
        系统每秒有tasks个任务需要处理，则需要的线程数为：tasks/(1/tasktime)，即tasks*tasktime个线程数。
        假设系统每秒任务数为10000~50000，每个任务耗时0.02秒，则需要10000*0.02至50000*0.02，即200~1000个线程。
        那么corePoolSize应该设置为大于200，具体数字最好根据8020原则，即80%情况下系统每秒任务数为20000，则corePoolSize可设置为400。
        queueCapacity:

        任务队列的长度要根据核心线程数，以及系统对任务响应时间的要求有关。
        队列长度可以设置为(corePoolSize/tasktime)*responsetime： (400/0.02)*0.05=1000，即队列长度可设置为1000。

        maxPoolSize:
        当系统负载达到最大值时，核心线程数已无法按时处理完所有任务，这时就需要增加线程。每秒10000个任务需要200个线程，那么当每秒达到50000个任务时，则需要(1000-queueCapacity)*(200/10000)，即60个线程，可将maxPoolSize设置为60。

         */
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);

        // rejection-policy：当pool已经达到max size的时候，如何处理新任务
        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }


    /**
     * 注册一个StatViewServlet
     *
     * @return
     */
    @Bean
    @Primary
    public ServletRegistrationBean DruidStatViewServlet() {
        //org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        //添加初始化参数：initParams
        //白名单：
        //servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        //IP黑名单 (存在共同时，deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
        //servletRegistrationBean.addInitParameter("deny", "192.168.9.216");
        //登录查看信息的账号密码.
        servletRegistrationBean.addInitParameter("loginUsername", "druid");
        servletRegistrationBean.addInitParameter("loginPassword", "druid");
        //是否能够重置数据 禁用HTML页面上的“Reset All”功能
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }


    /**
     * 注册一个：FilterRegistrationBean
     *
     * @return
     */
    @Bean
    @Primary
    public FilterRegistrationBean druidStatFilter() {

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        //添加过滤规则.
        filterRegistrationBean.addUrlPatterns("/*");
        //添加不需要忽略的格式信息.
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}
